package discovery

import (
	"common/config"
	"common/logs"
	"context"
	"encoding/json"
	clientv3 "go.etcd.io/etcd/client/v3"
	"time"
)

const (
	Etcd_Name = "etcd"
)

// EtcdRegister 服务注册到 Etcd
type EtcdRegister struct {
	etcdClient    *clientv3.Client                        // Etcd 客户端
	leaseId       clientv3.LeaseID                        // 租约 ID
	DialTimeout   int                                     // 超时时间
	ttl           int                                     // 租约时间
	keepAliveChan <-chan *clientv3.LeaseKeepAliveResponse // 心跳
	server        *Server                                 // 服务信息
	closeChan     chan struct{}                           // 关闭通道
}

func NewEtcdRegister() *EtcdRegister {
	return &EtcdRegister{
		DialTimeout: 3,
	}
}

// ServerRegister 创建连接并将服务注册到 Etcd 中
func (r *EtcdRegister) ServerRegister(conf config.EtcdConf) (err error) {
	// 1. 服务注册信息
	r.server = &Server{
		Name:      conf.Register.Name,
		Addr:      conf.Register.Addr,
		Weight:    conf.Register.Weight,
		Version:   conf.Register.Version,
		KeepALive: conf.Register.KeepAliveTtl,
	}
	// 2. 建立 Etcd 连接
	r.etcdClient, err = clientv3.New(clientv3.Config{
		Endpoints:   conf.Addrs,
		DialTimeout: time.Duration(r.DialTimeout) * time.Second,
	})
	if err != nil {
		return
	}
	// 3. 续约并绑定
	if err = r.grantAndBind(); err != nil {
		return
	}
	r.closeChan = make(chan struct{})
	// 4. 后台进行续约
	// TODO 如果 etcd 出现网络分区，续约过期不会自动续约
	go r.keepAliveWatch()
	logs.Info("etcd register success.")
	return
}

// Close 服务注销
func (r *EtcdRegister) Close() {
	r.closeChan <- struct{}{}
}

// keepAliveWatch 续约
func (r *EtcdRegister) keepAliveWatch() {
	//ticker := time.NewTicker(time.Duration(r.server.KeepALive) * time.Second)
	for {
		select {
		//case <-ticker.C:
		// 心跳检测进行续约
		/*if err := r.keepAlive(); err != nil {
			logs.Error("ticker register keepAlive failed.error:%v", err)
			// 重新进行续约
			if err = r.grantAndBind(); err != nil {
				logs.Error("%v", err)
			}
		}*/
		case <-r.keepAliveChan:
			logs.Debug("%v keepAlive success.", r.leaseId)
		case <-r.closeChan:
			if err := r.unRegister(); err != nil {
				logs.Error("close and unRegister error：%v", err)
			}
			// 租约撤销
			_, err := r.etcdClient.Revoke(context.Background(), r.leaseId)
			if err != nil {
				logs.Error("close and revoke lease error：%v", err)
			}
			// 关闭客户端
			if r.etcdClient != nil {
				if err := r.etcdClient.Close(); err != nil {
					logs.Error("close etcd client error：%v", err)
				}
			}
		}
	}
}

// register 服务注册
func (r *EtcdRegister) grantAndBind() (err error) {
	etcdTimeout, canal := context.WithTimeout(context.Background(), time.Duration(r.DialTimeout)*time.Second)
	defer canal()
	// 1. 创建租约
	if err = r.createLease(etcdTimeout, r.server.KeepALive); err != nil {
		return
	}
	// 2. 绑定租约
	if err = r.bindLease(etcdTimeout); err != nil {
		return
	}
	// 3. 进行心跳检测
	if err = r.keepAlive(); err != nil {
		return
	}
	return
}

// unRegister 服务注销
func (r *EtcdRegister) unRegister() error {
	_, err := r.etcdClient.Delete(context.Background(), r.server.BuildGrantKey())
	return err
}

// keepAlive 心跳检测
func (r *EtcdRegister) keepAlive() error {
	// keepAlive 是长连接
	resChan, err := r.etcdClient.KeepAlive(context.Background(), r.leaseId)
	if err != nil {
		logs.Error("keepAlive failed, error: %v", err)
		return err
	}
	r.keepAliveChan = resChan
	return nil
}

// bindLease 绑定租约
func (r *EtcdRegister) bindLease(ctx context.Context) error {
	jsonServer, _ := json.Marshal(r.server)
	_, err := r.etcdClient.Put(ctx, r.server.BuildGrantKey(), string(jsonServer), clientv3.WithLease(r.leaseId))
	if err != nil {
		logs.Error("bindLease etcd lease err:%v", err)
		return err
	}
	return nil
}

// createLease 创建租约 ttl：租约持续时间
func (r *EtcdRegister) createLease(ctx context.Context, ttl int64) error {
	// 创建租约超时时间是心跳检测时间 * 2
	grant, err := r.etcdClient.Grant(ctx, ttl*2)
	if err != nil {
		logs.Error("create etcd lease err:%v", err)
		return err
	}
	r.leaseId = grant.ID
	return nil
}
